package org.ngmj.demo1.severclientread;

import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.util.Iterator;

@Slf4j
public class Server {
    public static void main(String[] args) {
        //创建一个选择器
        try (Selector selector = Selector.open()) {
            //创建一个服务器通道
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            //设置为非阻塞模式
            serverSocketChannel.configureBlocking(false);
            //将选择器与服务器通道绑定
            SelectionKey register = serverSocketChannel.register(selector, 0, null);
            //设置选择器监听的IO事件类型（接受客户端连接）
            register.interestOps(SelectionKey.OP_ACCEPT);
            //绑定端口
            serverSocketChannel.bind(new InetSocketAddress(8080));
            while (true) {
                //选择器监听
                selector.select();
                //获取选择器监听的IO事件
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()) {
                    SelectionKey key = iterator.next();
                    //移除当前事件(需要手动移除，否则会一直存在)
                    iterator.remove();
                    log.info("key: {}", key);
                    //处理接受连接事件
                    if (key.isAcceptable()) {
                        ServerSocketChannel channel = (ServerSocketChannel) key.channel();
                        log.info("accept_channel: {}", channel);
                        SocketChannel sc = channel.accept();
                        log.info("sc: {}", sc);
                        sc.configureBlocking(false);
                        //将选择器与客户端通道绑定,将ByteBuffer作为附件，用于读取数据
                        ByteBuffer buffer = ByteBuffer.allocate(16);
                        SelectionKey scKey = sc.register(selector, SelectionKey.OP_READ, buffer);
                    } else if (key.isReadable()) {
                        try {
                            SocketChannel channel = (SocketChannel) key.channel();
                            ByteBuffer buffer = (ByteBuffer) key.attachment();
                            int read = channel.read(buffer);
                            //客户端正常断开，read返回-1
                            if (read == -1) {
                                key.cancel();
                            } else {
                                split(buffer);
                                //如果缓冲区已满，则创建新的缓冲区
                                if (buffer.position() == buffer.limit()) {
                                    buffer.flip();
                                    ByteBuffer newByteBuffer = ByteBuffer.allocate(buffer.capacity() * 2);
                                    newByteBuffer.put(buffer);
                                    //将新的缓冲区作为附件，用于下次读取数据
                                    key.attach(newByteBuffer);
                                }
                            }
                        } catch (Exception e) {
                            //如果客户端异常断开，则移除key，因为未正确处理，会不断加入selectedList
                            //所以需要手动将key取消掉
                            key.cancel();
                        }
                    }
                }
            }

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 处理粘包、半包
     * @param source
     */
    private static void split(ByteBuffer source) {
        source.flip();
        for (int i = 0; i < source.limit(); i++) {
            if (source.get(i) == '\n') {
                int length = i + 1 - source.position();
                ByteBuffer target = ByteBuffer.allocate(length);
                for (int j = 0; j < length; j++) {
                    target.put(source.get());
                }
                target.flip();
                log.info("get data: {}", Charset.defaultCharset().decode(target));
            }
        }
        source.compact();
    }
}
